﻿using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using T.FAS.Runtime.Base.Entity;

namespace T.FAS.Runtime.Metadata.Entity.DataModel
{
    /// <summary>
    /// 数据类型转换工具类
    /// </summary>
    public class DataTypeUtil
    {
        private static readonly Type s_databaseColumnType = typeof(DatabaseColumnType);
        private static readonly Type s_dataType = typeof(DataType);
        private static readonly Type s_databaseType = typeof(DatabaseType);
        private static readonly Dictionary<string, DataType> s_datatype = new Dictionary<string, DataType>
        {
            {"binary",      DataType.Binary},
            {"boolean",     DataType.Boolean},
            {"int",         DataType.Int},
            {"long",        DataType.Long},
            {"decimal",     DataType.Decimal},
            {"datetime",    DataType.DateTime},
            {"timestamp",   DataType.Timestamp},
            {"date",        DataType.Date},
            {"time",        DataType.Time},
            {"string",      DataType.String},
            {"record",      DataType.Record},
        };
        private static readonly Dictionary<string, DatabaseType> s_databaseMapping = new Dictionary<string, DatabaseType>
        {
            {"sqlserver",   DatabaseType.SQLServer},
            {"mysql",       DatabaseType.MySQL},
            {"es",          DatabaseType.ES},
            {"hbase",       DatabaseType.HBase},
            {"tidb",        DatabaseType.TiDB},
            {"postgresql",  DatabaseType.PostgreSQL},
            {"starrocks",   DatabaseType.StarRocks},
            {"druid",       DatabaseType.Druid},
            {"oracle",      DatabaseType.Oracle},
        };
        private static readonly Dictionary<string, DatabaseColumnType> s_databaseDataTypeMapping = new Dictionary<string, DatabaseColumnType>
        {
            {"bit",             DatabaseColumnType.Bit},
            {"tinyint",         DatabaseColumnType.TinyInt},
            {"smallint",        DatabaseColumnType.SmallInt},
            {"int",             DatabaseColumnType.Int},
            {"bigint",          DatabaseColumnType.BigInt},
            {"decimal",         DatabaseColumnType.Decimal},
            {"datetime",        DatabaseColumnType.DateTime},
            {"timestamp",       DatabaseColumnType.Timestamp},
            {"date",            DatabaseColumnType.Date},
            {"time",            DatabaseColumnType.Time},
            {"nchar",           DatabaseColumnType.Nchar},
            {"nvarchar",        DatabaseColumnType.Nvarchar},
            {"nvarchar(max)",   DatabaseColumnType.NvarcharMax},
            {"char",            DatabaseColumnType.Char},
            {"varchar",         DatabaseColumnType.Varchar},
            {"varchar(max)",    DatabaseColumnType.VarcharMax},
            {"binary",          DatabaseColumnType.Binary},
            {"varbinary",       DatabaseColumnType.Varbinary},
            {"varbinary(max)",  DatabaseColumnType.VarbinaryMax},
        };

        /// <summary>
        /// 将数据库物理列类型转换为String
        /// </summary>
        /// <param name="databaseColumnType"></param>
        /// <returns></returns>
        public static string ConvertDatabaseColumnTypeToString(DatabaseColumnType databaseColumnType)
        { 
            var result = Regex.Replace(databaseColumnType.ToString(), "Max", "(max)", RegexOptions.IgnoreCase);
            return result;
        }

        public static string ConvertDataTypeToString(DataType dataType)
        {
            return dataType.ToString();
        }

        /// <summary>
        /// 将字符串转换为数据库物理列类型枚举
        /// </summary>
        /// <param name="databaseDataType"></param>
        /// <returns></returns>
        public static DatabaseColumnType ConvertToDatabaseColumnType(string databaseDataType)
        {
            if (s_databaseDataTypeMapping.TryGetValue(databaseDataType, out DatabaseColumnType databaseColumnType))
            {
                return databaseColumnType;
            }
            else
            {
                var result = Regex.Replace(databaseDataType, "\\(Max\\)", "Max", RegexOptions.IgnoreCase);
                return (DatabaseColumnType)Enum.Parse(s_databaseColumnType, result, true);
            }
        }

        /// <summary>
        /// 将字符串转换为低代码应用程序类型枚举
        /// </summary>
        /// <param name="dataType"></param>
        /// <returns></returns>
        public static DataType ConvertToDataType(string dataType)
        {
            if (s_datatype.TryGetValue(dataType.ToLower(), out DataType type))
            {
                return type;
            }
            else
            {
                return (DataType)Enum.Parse(s_dataType, dataType);
            }
        }

        /// <summary>
        /// 将字符串转换为低代码支持的数据库类型枚举
        /// </summary>
        /// <param name="databaseType"></param>
        /// <returns></returns>
        public static DatabaseType ConvertToDatabaseType(string databaseType)
        {
            if (s_databaseMapping.TryGetValue(databaseType.ToLower(), out DatabaseType type))
            {
                return type;
            }
            else
            {
                return (DatabaseType)Enum.Parse(s_databaseType, databaseType);
            }
        }

        /// <summary>
        /// 将数据库类型转换为应用程序类型
        /// </summary>
        /// <param name="databaseColumnType"></param>
        /// <returns></returns>
        public static DataType ConvertDataTypeFromDatabaseColumnType(DatabaseColumnType databaseColumnType)
        {
            switch (databaseColumnType)
            {
                case DatabaseColumnType.Bit:
                    return DataType.Boolean;
                   
                case DatabaseColumnType.TinyInt:
                    return DataType.Int;
                   
                case DatabaseColumnType.SmallInt:
                    return DataType.Int;
                   
                case DatabaseColumnType.Int:
                    return DataType.Int;
                   
                case DatabaseColumnType.BigInt:
                    return DataType.Long;
                   
                case DatabaseColumnType.Decimal:
                    return DataType.Decimal;
                   
                case DatabaseColumnType.DateTime:
                    return DataType.DateTime;
                   
                case DatabaseColumnType.Timestamp:
                    return DataType.Timestamp;
                   
                case DatabaseColumnType.Date:
                    return DataType.Date;
                   
                case DatabaseColumnType.Time:
                    return DataType.Time;
                   
                case DatabaseColumnType.Nchar:
                    return DataType.String;
                   
                case DatabaseColumnType.Nvarchar:
                    return DataType.String;
                   
                case DatabaseColumnType.NvarcharMax:
                    return DataType.String;
                   
                case DatabaseColumnType.Char:
                    return DataType.String;
                   
                case DatabaseColumnType.Varchar:
                    return DataType.String;
                   
                case DatabaseColumnType.VarcharMax:
                    return DataType.String;
                   
                case DatabaseColumnType.Binary:
                    return DataType.Binary;
                   
                case DatabaseColumnType.Varbinary:
                    return DataType.Binary;
                   
                case DatabaseColumnType.VarbinaryMax:
                    return DataType.Binary;
                   
                default:
                    throw new FasException(DataModelErrorCode.DataType.TypeMappingError, $"数据库类型转换有问题");
            }
        }

        /// <summary>
        /// 获取应用程序数据类型的默认数据库数据类型
        /// </summary>
        /// <param name="dataType"></param>
        /// <returns></returns>
        public static DatabaseColumnType ConvertDatabaseColumnTypeFromDataType(DataType dataType)
        {
            switch (dataType)
            {
                case DataType.Binary:
                    return DatabaseColumnType.Binary;
                case DataType.Boolean:
                    return DatabaseColumnType.Bit;
                case DataType.Int:
                    return DatabaseColumnType.Int;
                case DataType.Long:
                    return DatabaseColumnType.BigInt;
                case DataType.Decimal:
                    return DatabaseColumnType.Decimal;
                case DataType.DateTime:
                    return DatabaseColumnType.DateTime;
                case DataType.Timestamp:
                    return DatabaseColumnType.DateTime;
                case DataType.Date:
                    return DatabaseColumnType.Date;
                case DataType.Time:
                    return DatabaseColumnType.Time;
                case DataType.String:
                    return DatabaseColumnType.Varchar;
                case DataType.Record:
                case DataType.List:
                default:
                    throw new Exception($"不支持的DO列数据类型:{dataType}");
            }
        }

        /// <summary>
        /// 校验dataType与databaseColumnType是否匹配
        /// </summary>
        /// <param name="datatype"></param>
        /// <param name="databaseColumnType"></param>
        /// <returns></returns>
        public static bool IsMatch(string datatype, string databaseColumnType)
        {
            try
            {
                var DBColType = ConvertToDatabaseColumnType(databaseColumnType);
                var APPDataType = ConvertToDataType(datatype);
                return ConvertDataTypeFromDatabaseColumnType(DBColType) == APPDataType;
            }
            catch (Exception e)
            {
                throw new FasException(DataModelErrorCode.DataType.TypeMappingError, $"APP类型:{datatype}, DBColType:{databaseColumnType}匹配失败", e);
            }
        }

        /// <summary>
        ///  将字符串转换为低代码数据库数据类型枚举
        /// </summary>
        /// <param name="databaseDataType"></param>
        /// <param name="databaseColumnType"></param>
        /// <returns></returns>
        public static bool TryConvertToDatabaseColumnType(string databaseDataType, out DatabaseColumnType databaseColumnType)
        {
            databaseColumnType = DatabaseColumnType.Varchar;
            try
            {
                if (!string.IsNullOrEmpty(databaseDataType))
                    return s_databaseDataTypeMapping.TryGetValue(databaseDataType.ToLower(), out databaseColumnType);
                return true;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// 将字符串转换为低代码应用程序类型枚举
        /// </summary>
        /// <param name="dataType"></param>
        /// <returns></returns>
        public static bool TryConvertToDataType(string dataType, out DataType type)
        {
            type = DataType.String;
            try
            {
                if (!string.IsNullOrEmpty(dataType))
                    return s_datatype.TryGetValue(dataType.ToLower(), out type);
                return true;
            }
            catch
            {
                return false;
            }
        }
    }
}
